package com.springBoot.springBootSysCore.common.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.springBoot.springBootSysCore.common.config.Global;
import com.springBoot.springBootSysCore.common.third.oss.OssUpload;
import com.springBoot.springBootSysCore.modules.entity.system.SysFileTmp;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

/**
 * <p style="color:blue"> 用于文件上传 包括图片视频等</p>
 *
 * @author moon
 *
 */
public class FileUtil {
    private static String ossIsOpen = Global.getConfig("oss.isOpen");

    public static String getOssIsOpen() {
        return ossIsOpen;
    }

    public static void setOssIsOpen(String ossIsOpen) {
        FileUtil.ossIsOpen = ossIsOpen;
    }

    /**
    * 上传文件,是多文件上传,文件验证就在前端验证.比如限制文件上传格式
    * 需要修改上传文件大小的话,就修改配置文件spring-mvc.xml
    * @param goalFolder 目标文件夹（上传的目的地）
    * @param request
    * @return 返回上传后的新文件路径
    */
    public static Map<String,String> upLoadFile(String goalFolder,HttpServletRequest request) {
//        Session s = SessionUtils.getSubject().getSession(false);
        CommonsMultipartResolver mutilpartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        Map<String,String> retMap = new HashMap<String,String>();
        
        // request如果是Multipart类型、
        if (mutilpartResolver.isMultipart(request)) {
            // 强转成 MultipartHttpServletRequest
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            String projectAddress = request.getSession().getServletContext().getRealPath("/"+goalFolder);
            Iterator<String> it = multiRequest.getFileNames();
            while (it.hasNext()) {
            	String textName = it.next();
                // 获取MultipartFile类型文件
                List<MultipartFile> fileList = multiRequest.getFiles(textName);
                for(MultipartFile fileDetail:fileList){
                	if (fileDetail != null && fileDetail.getSize() >= 1) {
                    	String path = getFileAddress(fileDetail, projectAddress);
                        @SuppressWarnings("unused")
						File localFile = new File(path);

                        path = path.replaceAll("\\\\", "/");
                        try {
                            // 将上传文件写入到指定文件出、核心！
                            //fileDetail.transferTo(localFile);
                            // 非常重要、有了这个想做什么处理都可以 ,可以自己处理
                            InputStream in =  fileDetail.getInputStream();
                            FileOutputStream out = new FileOutputStream(path);
                            int b = 0;
                            while((b = in.read()) != -1){
                                out.write(b);
                            }
                            in.close();
                            out.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if(StringUtils.isNoneBlank(FileUtil.getOssIsOpen()) && "true".equals(FileUtil.getOssIsOpen())){
                            path = OssUpload.upload(localFile).toString();
                            localFile.delete();
                        }else{
                            path = path.substring(path.indexOf(goalFolder));
                        }
                        if(retMap.get(textName) == null){
                        	retMap.put(textName, path);
                        }else{
                        	retMap.put(textName, retMap.get(textName)+","+path);
                        }
                    }
                }
            }
        }
        return retMap;

    }

    /**
     * 创建或者获取文件目录,并且修改文件名称
     *
     * @param fileDetail
     * @return
     */
    private static String getFileAddress(MultipartFile fileDetail,String projectAddress) {
        String[] fileNames = fileDetail.getOriginalFilename().split("\\.");
        String fileNamedir = projectAddress + "/";
        File localFileDir = new File(fileNamedir);
        if (!localFileDir.exists()) {
            localFileDir.mkdirs();
        }
        String fileName = UUID.randomUUID().toString().replace("-", "") + "." + fileNames[fileNames.length - 1];
        String path = fileNamedir + fileName;
        return path;
    }

    /**
     * 在网上下载东西
     *
     * @param response
     * @param destUrl
     *            目标下载地址
     * @throws MalformedURLException
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    public  static void downUrlFile(HttpServletResponse response, String destUrl)
            throws MalformedURLException, IOException,
            UnsupportedEncodingException {
        int param=destUrl.lastIndexOf("/");
        String fileName=destUrl.substring(param+1, destUrl.length());
        // 建立链接  
        URL url = new URL(destUrl);
        HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();
        // 连接指定的资源  
        httpUrl.connect();
        // 获取网络输入流  
        BufferedInputStream bis = new BufferedInputStream(httpUrl.getInputStream());

        response.setContentType("application/x-msdownload");
        response.setHeader("Content-Disposition", "attachment; filename="+java.net.URLEncoder.encode(fileName,"UTF-8"));
        OutputStream out = response.getOutputStream();
        byte[] buf = new byte[1024];
        if (destUrl != null) {
            BufferedInputStream br = bis;
            int len = 0;
            while ((len = br.read(buf)) > 0){
                out.write(buf, 0, len);
            }
            br.close();
        }
        out.flush();
        out.close();
    }

    /**
     * 根据文件路径删除文件
     * @param request
     * @param filePath 文件路径
     * @return 是否删除成功
     */
    public static boolean delFileByPath(HttpServletRequest request,String filePath){
    	 String path = request.getSession().getServletContext().getRealPath("/")+"/"+filePath;
    	 File file = new File(path);  
		if (file.exists()) {
			file.delete();
			return true;
		}
		return false;
    }
    
    
    /** 
     * 复制单个文件 
     *  
     * @param srcFileName 
     *            待复制的文件名 
     * @param descFileName 
     *            目标文件名 
     * @param overlay 
     *            如果目标文件存在，是否覆盖 
     * @return 如果复制成功返回true，否则返回false 
     */  
    public static boolean copyFile(String srcFileName, String destFileName,boolean overlay) {  
        File srcFile = new File(srcFileName);  
  
        // 判断源文件是否存在  
        if (!srcFile.exists()) {  
        	System.err.println("源文件：" + srcFileName + "不存在！");
        	return false;  
        } else if (!srcFile.isFile()) {  
            System.err.println("复制文件失败，源文件：" + srcFileName + "不是一个文件！");
            return false;  
        }  
  
        // 判断目标文件是否存在  
        File destFile = new File(destFileName);  
        if (destFile.exists()) {  
            // 如果目标文件存在并允许覆盖  
            if (overlay) {  
                // 删除已经存在的目标文件，无论目标文件是目录还是单个文件  
                new File(destFileName).delete();  
            }  
        } else {  
            // 如果目标文件所在目录不存在，则创建目录  
            if (!destFile.getParentFile().exists()) {  
                // 目标文件所在目录不存在  
                if (!destFile.getParentFile().mkdirs()) {  
                    // 复制文件失败：创建目标文件所在目录失败  
                    return false;  
                }  
            }  
        }  
  
        // 复制文件  
        int byteread = 0; // 读取的字节数  
        InputStream in = null;  
        OutputStream out = null;  
  
        try {  
            in = new FileInputStream(srcFile);  
            out = new FileOutputStream(destFile);  
            byte[] buffer = new byte[1024];  
  
            while ((byteread = in.read(buffer)) != -1) {  
                out.write(buffer, 0, byteread);  
            }  
            return true;  
        } catch (FileNotFoundException e) {  
            return false;  
        } catch (IOException e) {  
            return false;  
        } finally {  
            try {  
                if (out != null)  
                    out.close();  
                if (in != null)  
                    in.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    /** 
     * 复制整个目录的内容 
     *  
     * @param srcDirName 
     *            待复制目录的目录名 
     * @param destDirName 
     *            目标目录名 
     * @param overlay 
     *            如果目标目录存在，是否覆盖 
     * @return 如果复制成功返回true，否则返回false 
     */  
    public static boolean copyDirectory(String srcDirName, String destDirName,  
            boolean overlay) {  
        // 判断源目录是否存在  
        File srcDir = new File(srcDirName);  
        if (!srcDir.exists()) {  
            System.err.println("复制目录失败：源目录" + srcDirName + "不存在！");
            return false;  
        } else if (!srcDir.isDirectory()) {  
        	System.err.println("复制目录失败：" + srcDirName + "不是目录！");
            return false;  
        }  
  
        // 如果目标目录名不是以文件分隔符结尾，则加上文件分隔符  
        if (!destDirName.endsWith(File.separator)) {  
            destDirName = destDirName + File.separator;  
        }  
        File destDir = new File(destDirName);  
        // 如果目标文件夹存在  
        if (destDir.exists()) {  
            // 如果允许覆盖则删除已存在的目标目录  
            if (overlay) {  
                new File(destDirName).delete();  
            } else {  
            	System.err.println("复制目录失败：目的目录" + destDirName + "已存在！");
                return false;  
            }  
        } else {  
            // 创建目的目录  
            System.out.println("目的目录不存在，准备创建。。。");  
            if (!destDir.mkdirs()) {  
                System.out.println("复制目录失败：创建目的目录失败！");  
                return false;  
            }  
        }  
  
        boolean flag = true;  
        File[] files = srcDir.listFiles();  
        for (int i = 0; i < files.length; i++) {  
            // 复制文件  
            if (files[i].isFile()) {  
                flag = copyFile(files[i].getAbsolutePath(),  
                        destDirName + files[i].getName(), overlay);  
                if (!flag)  
                    break;  
            } else if (files[i].isDirectory()) {  
                flag = copyDirectory(files[i].getAbsolutePath(),  
                        destDirName + files[i].getName(), overlay);  
                if (!flag)  
                    break;  
            }  
        }  
        if (!flag) {  
        	System.err.println("复制目录" + srcDirName + "至" + destDirName + "失败！"); 
            return false;  
        } else {  
            return true;  
        }  
    }
    /**
     * 删除文件或文件夹(包括文件夹中的子文件)
     * @param delpath
     * @return
     * @throws Exception
     */
    public static boolean deleteAllFile(String delpath){
        File file = new File(delpath);
        if(!file.exists()){
            return false;
        }
        if (!file.isDirectory()) {
            file.delete();
        } else if (file.isDirectory()) {
            String[] filelist = file.list();
            for (int i = 0; i < filelist.length; i++) {
                File delfile = new File(delpath + "\\" + filelist[i]);
                if (!delfile.isDirectory()) {
                    delfile.delete();
                } else if (delfile.isDirectory()) {
                    deleteAllFile(delpath + "\\" + filelist[i]);
                }
            }
            file.delete();
        }
        return true;
    }
    /**
     * 删除指定的文件或文件夹<br/>
     * 如果传入的是文件夹路径,并且文件夹下面有子文件,删除失败
     * @return
     */
    public static boolean deleteFile(String delPath){
        if(org.apache.commons.lang3.StringUtils.isBlank(delPath)){
            return false;
        }
        File file = new File(delPath);
        if (!file.exists()){
            return false;
        }
        if (file.isDirectory()&&file.list().length>0) {
            return false;
        }
        file.delete();
        return true;
    }
    /*********************************************************/
    /*********************************************************/
    /*********************************************************/
    /**
     * spring MVC Ajax单文件上传 知道input file 的name可以用此方法
     * @param multipartFile
     * @param filePath 必填项,要存放的路径,不带文件名(d:/img  d盘的img文件夹下)
     * @param fileName 要删除的原（以前数据库）文件名字， 如果没有就不传
     * @param suffixs 必填项, 限制的后缀 如:.jpg .mp4 （集合内容统一小写并且带点）;
     * @param maxSize 单个文件最大大小,单位MB 为 0 不限制;
     * @return 返回： Map中有name path size error updateTime(Date)信息
     * @throws IOException
     * @throws IllegalStateException
     */
    public static Map<String,Object> springAjax_one(MultipartFile multipartFile,String filePath,String fileName,List<String> suffixs,int maxSize) throws IllegalStateException, IOException{
        Map<String,Object> map = new HashMap<>();
        if(multipartFile!=null){
            long size = multipartFile.getSize();//获取文件大小
            String inputFileName =multipartFile.getOriginalFilename().toLowerCase();
            String ext = inputFileName.substring(inputFileName.lastIndexOf(".")).toLowerCase();//后缀名
            if(!suffixs.isEmpty()&&!suffixs.contains(ext)){
                map.put("error", "文件类型不正确!");
            }else if(maxSize<=0||size<=maxSize*1024*1024){   //maxSize<=0不限时大小   maxSize*1024*1024 Mb
                File directoFile = new File(filePath);	//要上传到的文件夹的路径
                if(!directoFile.exists()){
                    directoFile.mkdirs(); //如果文件夹不存在就创建
                }
                if(org.apache.commons.lang3.StringUtils.isBlank(fileName)){
                    //重新获得名称
                    fileName = getFileNameByPath(null);
                }
                if(fileName.indexOf("?")!=-1){
                    fileName = fileName.substring(0, fileName.lastIndexOf("?"));
                }
                fileName = getFileNameByPath(fileName);
                /**删除原文件，生成新文件名称**/
                File file = new File(directoFile, fileName);//在文件夹里创建一个文件
                if(file.exists()&&file.isFile()){
                    file.delete();//如果有这个文件就删除
                }
                fileName = removeFileNameExt(fileName)+ext;//如果没有传名字，在生成一个名称
                file = new File(directoFile, fileName);//重新创建这个文件

                multipartFile.transferTo(file);
                map.put("name", inputFileName.substring(0, inputFileName.indexOf(ext)));//上传原文件的名称
                map.put("dataName", fileName);//当前数据库需要保存的文件名称
                map.put("path", filePath+File.separator+fileName);//文件在服务器中的完整路径
                map.put("size", size);//文件大小  b
                map.put("ext", ext);//后缀
                map.put("updateTime", new Date());//上传时间
            }else{
                map.put("error", "文件过大!");
            }
        }
        return map;
    }
    /**
     * spring MVC Ajax文件上传
     * @param filePath 必填项,要存放的路径,不带文件名(d:/img  d盘的img文件夹下)
     * @param suffixs 必填项,限制的后缀 如:.jpg .mp4 （集合内容统一小写并且带点）;
     * @param maxSize 单个文件最大大小,单位MB 为 0 不限制;
     * @return 返回类型：List<Map<String,Object>> Map中有 name path size error updateTime(Date)信息
     * @throws IOException
     * @throws IllegalStateException
     */
    public static List<Map<String,Object>> springAjax_many(List<MultipartFile> li,String filePath, List<String> suffixs,int maxSize) throws IllegalStateException, IOException{
        List<Map<String,Object>> list = new ArrayList<>();
        if(li.size()>0){
            for (MultipartFile multipartFile : li) {
                String fileName=UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10);
                Map<String,Object> map = new HashMap<>();
                long size = multipartFile.getSize();
                String inputFileName = multipartFile.getOriginalFilename().toLowerCase();//上传文件文件名称
                if(org.apache.commons.lang3.StringUtils.isBlank(inputFileName)){
                    return null;
                }
                String ext = inputFileName.substring(inputFileName.lastIndexOf(".")).toLowerCase();//后缀名
                if (multipartFile.getOriginalFilename().length() > 0) {	//判断文件是否存在
                    //判断文件类型
                    if(!suffixs.isEmpty()&&!suffixs.contains(ext)){
                        map.put("error", "文件类型不正确!");
                    }
                    //判断大小
                    else if(maxSize<=0||size<=maxSize*1024*1024){
                        File directoFile = new File(filePath);
                        if(!directoFile.exists()){
                            directoFile.mkdirs();
                        }
                        File file = new File(directoFile, fileName);
                        if(file.exists()&&file.isFile()){
                            file.delete();
                        }
                        fileName = removeFileNameExt(fileName)+ext;
                        file = new File(directoFile, fileName);
                        multipartFile.transferTo(file);
                        map.put("name", inputFileName.substring(0, inputFileName.indexOf(ext)));
                        map.put("dataName", fileName);
                        map.put("path", filePath+File.separator+fileName);
                        map.put("size", size);
                        map.put("ext", ext);
                        map.put("updateTime", new Date());
                    }else
                        map.put("error", "文件过大!");
                }else{
                    map.put("error", "未选择文件!");
                }
                list.add(map);
            }
        }
        return list;
    }
    /**
     * spring MVC Ajax文件上传
     * @param filePath 必填项,要存放的路径,不带文件名(d:/img  d盘的img文件夹下)
     * @param fileName 保存的文件名字，可指定，也可不指定
     * @param suffixs 必填项,限制的后缀 如:.jpg .mp4 （集合内容统一小写并且带点）;
     * @param maxSize 单个文件最大大小,单位MB 为 0 不限制;
     * @return 返回类型：List<Map<String,Object>> Map中有 name path size error updateTime(Date)信息
     * @throws IOException
     * @throws IllegalStateException
     */
    public static SysFileTmp springAjax_oneToTemp(MultipartFile multipartFile, String filePath, String fileName, List<String> suffixs, int maxSize) throws IllegalStateException, IOException{
        SysFileTmp fileTmp = new SysFileTmp();
        if(multipartFile!=null){
            long size = multipartFile.getSize();//获取文件大小
            String inputFileName =multipartFile.getOriginalFilename().toLowerCase();
            String ext = inputFileName.substring(inputFileName.lastIndexOf(".")).toLowerCase();//后缀名
            if(!suffixs.isEmpty()&&!suffixs.contains(ext)){
                fileTmp.setError("文件类型不正确!");
            }else if(maxSize<=0||size<=maxSize*1024*1024){   //maxSize<=0不限时大小   maxSize*1024*1024 Mb
                File directoFile = new File(filePath);	//要上传到的文件夹的路径
                if(!directoFile.exists()){
                    directoFile.mkdirs(); //如果文件夹不存在就创建
                }
                if(StringUtils.isBlank(fileName)){
                    //重新获得名称
                    fileName = getFileNameByPath(null);
                }
                if(fileName.indexOf("?")!=-1){
                    fileName = fileName.substring(0, fileName.lastIndexOf("?"));
                }
                fileName = getFileNameByPath(fileName);
                /**删除原文件，生成新文件名称**/
                File file = new File(directoFile, fileName);//在文件夹里创建一个文件
                if(file.exists()&&file.isFile()){
                    file.delete();//如果有这个文件就删除
                }
                fileName = removeFileNameExt(fileName)+ext;//如果没有传名字，在生成一个名称
                file = new File(directoFile, fileName);//重新创建这个文件
                multipartFile.transferTo(file);

                fileTmp.setName(inputFileName.substring(0, inputFileName.indexOf(ext)));
                fileTmp.setDataName(fileName);
                fileTmp.setPath((filePath+File.separator+fileName).replace("\\", "/"));
                fileTmp.setSize(String.valueOf(size));;
                fileTmp.setExt(ext);
            }else{
                fileTmp.setError("文件过大!");
            }
        }
        return fileTmp;
    }

    /**
     * 通过一个 文件 路径获取文件名称
     * @param filePath  文件路径("teacher/imgs/4bbf2117d761.jpg")
     * @return 文件名 (4bbf2117d761.jpg)
     */
    public static String getFileNameByPath(String filePath){
        if(filePath==null||"".equals(filePath)){
            return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10);
        }
        filePath = filePath.replace("\\\\", "/");
        filePath = filePath.replace("\\", "/");
        String[] filePaths = filePath.split("/");
        String fileName = ".";
        if(filePaths.length>0){
            fileName = filePaths[filePaths.length-1];
        }else{
            fileName=filePaths[0];
        }
        return fileName;
    }
    /**
     * 去掉后缀名
     * @return
     */
    private static String removeFileNameExt(String fileName){
        if(fileName==null||"".equals(fileName)){
            return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 10);
        }
        if(fileName.indexOf(".")!=-1)
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
        return fileName;
    }

    // 验证字符串是否为正确路径名的正则表达式
    @SuppressWarnings("unused")
    private static String matches = "[A-Za-z]:\\\\[^:?\"><*]*";
    // 通过 sPath.matches(matches) 方法的返回值判断是否正确
    // sPath 为路径字符串
    /**
     *  根据路径删除指定的目录或文件，无论存在与否
     *@param sPath  要删除的目录或文件
     *@return 删除成功返回 true，否则返回 false。
     */
    public static boolean DeleteFolder(String sPath) {
        boolean	flag = false;
        File file = new File(sPath);
        // 判断目录或文件是否存在
        if (!file.exists()) {  // 不存在返回 false
            return flag;
        } else {
            // 判断是否为文件
            if (file.isFile()) {  // 为文件时调用删除文件方法
                return deleteFile(sPath);
            } else {  // 为目录时调用删除目录方法
                return deleteDirectory(sPath);
            }
        }
    }



    /**
     * 删除目录（文件夹）以及目录下的文件
     * @param   sPath 被删除目录的文件路径
     * @return  目录删除成功返回true，否则返回false
     */
    public static boolean deleteDirectory(String sPath) {
        //如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            //删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) break;
            } //删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) break;
            }
        }
        if (!flag) return false;
        //删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

}
